#include "types.h"
#include "defs.h"
#include "param.h"
#include "mmu.h"
#include "proc.h"
#include "fs.h"
#include "file.h"
#include "fcntl.h"
#include "traps.h"

struct{
  struct map maparray[NOMAP];
}mapstruct;

//Apaga el bit w de la pagina que comienza en basepage
static void
bitwoff(char* basepage){                         
   clearptew(proc->pgdir,basepage);          
}

//Actualiza los valores de la estructura "mapstruct"
static void
updatemapinfo(int index, int mode, uint base, uint size ,struct inode *ip , int state)
{
  mapstruct.maparray[index].state = state;
  mapstruct.maparray[index].base = base;
  mapstruct.maparray[index].size = size;
  mapstruct.maparray[index].mode = mode;
  mapstruct.maparray[index].ip = ip;
}

//Resetear los valores de la estructura "mapstruct"
static void
clearmap(int index)
{
  updatemapinfo(index,0,0,0,0,M_FREE);
}

//Retorna el indice al primer map disponible
static int
mapfreeindex()
{
  int i;
  
  for(i=0; i < NOMAP; i++ ) {
    if(proc->pmap[i]->state != M_BUSY)
      return i;
 }
 return T_FULLMAP;
}

//Retorna el indice del mapeo donde se encuentra address
int
mapindex(uint address)
{
  int i;
  for(i=0; i < NOMAP;i++){ 
    if((proc->pmap[i]->base<=address)&&(proc->pmap[i]->size>=address))
      return i;
  }    
  return T_NOMAPROC;
}

void
munmap(char* addrmunmap)
{
  int j;
  int bytewrited;
  int index;
  uint pte;
 
  index = mapindex((int)addrmunmap);
  for(j=0;((j+(int)addrmunmap<proc->pmap[index]->size)&&(j+(int)addrmunmap<proc->sz));j+=PGSIZE){
    begin_trans();  
    pte = walkpgdirtwo(proc->pgdir,addrmunmap+j,0);
    if(pte & PTE_D){
      if( j + PGSIZE >= proc->pmap[index]->ip->size)
         bytewrited = writei(proc->pmap[index]->ip,addrmunmap+j,j,(proc->pmap[index]->ip->size-j));
       else        
         bytewrited = writei(proc->pmap[index]->ip,addrmunmap+j,j,PGSIZE);
       commit_trans();
       if(bytewrited==-1)
        cprintf("munmap error: bytewrited fail\n");
    }
    commit_trans();
  }
  if (proc->sz == proc->pmap[index]->size)
    proc->sz = deallocuvm(proc->pgdir,proc->pmap[index]->size,proc->pmap[index]->base);
  clearmap(index);
}

char*
mmap(char* filename,int mode)
{
  char* map;
  int freeindex;
  struct inode *ip;
  
  if((ip = namei(filename)) == 0){
    cprintf("munmap error: wrong filename\n");
    return 0;
  }
  if((freeindex = mapfreeindex()) == T_FULLMAP){
    cprintf("munmap error: full map \n");
    return 0;
  }
  ilock(ip);
  mapingfiles(proc->pgdir,proc->sz,mode);
  if (freeindex == 0){
    updatemapinfo(freeindex,mode,proc->sz, PGROUNDDOWN(proc->sz+ip->size) ,ip,M_BUSY);
  }
  else{
    updatemapinfo(freeindex,mode,proc->pmap[freeindex-1]->size,
        PGROUNDDOWN(proc->pmap[freeindex-1]->size+ip->size ),ip,M_BUSY);
  }
  proc->pmap[freeindex]   = &mapstruct.maparray[freeindex];
  map = (char*)proc->pmap[freeindex]->base; 
  iunlock(ip);
  return map;
}


//Realiza la carga de la pagina faltante donde
//se encuentra cr2.
int
loadpage(uint cr2)
{
  int idpage;
  int inodeoffset;
  char* basepagefault;
  int bitestoread;
  int index;
  
  index = mapindex(cr2);
  if((cr2>proc->pmap[index]->size)||(cr2<proc->pmap[index]->base)){
    cprintf("munmap error: incorrect address access");
    return T_OUTMAP;
  }
  idpage = PTX(cr2);
  inodeoffset = idpage - PTX(proc->pmap[index]->base); 
  basepagefault  = (char*)(idpage*PGSIZE);
  if ((uint)basepagefault >= proc->sz)
    proc->sz = allocuvm(proc->pgdir,(uint)basepagefault,(uint)(basepagefault+PGSIZE));
  else
    allocuvm(proc->pgdir,(uint)basepagefault,(uint)(basepagefault+PGSIZE));
  if(proc->pmap[index]->mode==O_RDONLY)
    bitwoff(basepagefault);       
  bitestoread = proc->pmap[index]->ip->size-inodeoffset*PGSIZE ;
  if(bitestoread> PGSIZE)
    bitestoread = PGSIZE;
  if(loaduvm(proc->pgdir,basepagefault,proc->pmap[index]->ip,inodeoffset*PGSIZE,bitestoread) != 0)
    cprintf("loadpage error\n");
  return 0;
}
